const crypto = require('crypto');
const fs = require("fs");

/**
 * @param {String} type
 * @param {String | Buffer} originString 
 */
function hash(type, originString) {
	const hash = crypto.createHash(type);
	hash.update(originString);
	return hash.digest('hex');
}
/**
 * 填充补位 encode
 * @param {String} data 需要进行填充补位的明文
 */
function pkcs7Pad(data) {
	// 计算需要填充的位数
	const padLength = 32 - (data.length % 32);
	// Buffer.alloc：申请指定大小的内存，并且清除原数据，默认填充 0
	// Buffer.allocUnsafe：申请指定大小内存，但不清除原数据，速度更快
	const result = Buffer.allocUnsafe(padLength); // Buffer.alloc
	result.fill(padLength);
	return Buffer.concat([data, result]);
}
/**
 * 删除补位 decode
 * @param {String} data 解密后的明文
 */
function pkcs7Unpad(data) {
	const padLength = data[data.length - 1];
	/* istanbul ignore if */
	if (padLength < 1 || padLength > 32) {
		return data;
	}
	return data.slice(0, data.length - padLength);
}
/**
 * 解码并校验 encodingAESKey
 * @param encodingAESKey EncodingAESKey
 */
function parseEncodingAESKey(encodingAESKey) {
	const key = Buffer.from(`${encodingAESKey}=`, 'base64');
	/* istanbul ignore if */
	if (key.length !== 32) {
		throw new Error('invalid encodingAESKey');
	}
	const iv = key.slice(0, 16);
	return {
		key,
		iv
	};
}
/**
 * getSignStr根据object转换签名字符串
 * @param {Object} obj
 */
function getSignStr(obj) {
	return Object.keys(obj)
		.filter(key => key !== 'sign' && obj[key] !== undefined && obj[key] !== '')
		.sort()
		.map(key => key + '=' + obj[key])
		.join('&')
}
/**
 * getNonceStr获得随机字符串
 * @param {int} length
 */
function getNonceStr(length = 16) {
	let str = ''
	while (str.length < length) {
		str += Math.random().toString(32).substring(2)
	}
	return str.substring(0, length)
}

module.exports = {
	/**
	 * getSignStr根据object转换签名字符串
	 * @param {Object} obj
	 */
	getSignStr,
	/**
	 * getNonceStr获得随机字符串
	 * @param {int} length
	 */
	getNonceStr,
	/**
	 * 填充补位 encode
	 * @param {String} data 需要进行填充补位的明文
	 */
	pkcs7Pad,
	/**
	 * 删除补位 decode
	 * @param {String} data 解密后的明文
	 */
	pkcs7Unpad,
	/**
	 * 将base64字符串转换为普通字符串
	 * @param {String} data
	 */
	fromBase64(data) {
		return Buffer.from(data, 'base64').toString('utf-8');
	},
	/**
	 * 将普通字符串转换为base64字符串
	 * @param {String} data
	 */
	toBase64(data) {
		return data.toString("base64");
	},
	/**
	 * 获取签名
	 * @param token     Token
	 * @param timestamp 时间戳
	 * @param nonce     随机串
	 * @param encrypt   密文
	 */
	getSignature: function(token, timestamp, nonce, encrypt) {
		const arr = [token, timestamp, nonce];
		if (encrypt) {
			arr.push(encrypt);
		}
		return crypto.createHash('sha1')
			.update(arr.sort().join(''))
			.digest('hex');
	},
	/**
	 * 获取 JSAPI 签名
	 * @param options 签名相关参数
	 */
	getJsApiSignature: function(options) {
		const {
			url,
			ticket
		} = options;
		const nonceStr = options.nonceStr || Math.random().toString(36).slice(2);
		const timestamp = options.timestamp || Math.floor(Date.now() / 1000);
		const signature = crypto.createHash('sha1')
			.update(`jsapi_ticket=${ticket}&noncestr=${nonceStr}&timestamp=${timestamp}&url=${url}`)
			.digest('hex');
		return {
			timestamp,
			nonceStr,
			signature
		};
	},
	/**
	 * MD5加密
	 *  @param {String | Buffer} originString 
	 */
	md5: function(originString) {
		return hash('md5', originString);
	},
	/**
	 * sha1加密
	 * @param {String | Buffer} originString
	 */
	sha1: function(originString) {
		return hash('sha1', originString);
	},
	hmac: function(originString, secretKey) {
		const hmac = crypto.createHmac('sha256', secretKey);
		hmac.update(originString);
		return hmac.digest("hex");
	},
	/**
	 * AES加密
	 * @param {String} originString
	 * @param {String} secretKey
	 */
	aes192Encrypt: function(originString, secretKey) {
		const cipher = crypto.createCipher('aes192', secretKey);
		var crypted = cipher.update(originString, 'utf8', 'hex');
		return crypted + cipher.final('hex');
	},
	/**
	 * AES解密
	 * @param {Object} encrypted
	 * @param {Object} key
	 */
	aes192Decrypt: function(encrypted, secretKey) {
		const decipher = crypto.createDecipher('aes192', secretKey);
		var decrypted = decipher.update(encrypted, 'hex', 'utf8');
		return decrypted + decipher.final('utf8');
	},
	/**
	 * aes-256-cbc加密
	 * @param encodingAESKey EncodingAESKey
	 * @param message        消息文本
	 * @param id             ID
	 * @param random         随机串
	 */
	aes256cbcEncrypt: function(encodingAESKey, message, id, random = crypto.randomBytes(16)) {
		const {
			key,
			iv
		} = parseEncodingAESKey(encodingAESKey);
		const msg = Buffer.from(message);
		const msgLength = Buffer.allocUnsafe(4);
		msgLength.writeUInt32BE(msg.length, 0);
		// console.log('aes256cbcEncrypt>>>111',random);
		// console.log('aes256cbcEncrypt>>>222',Buffer.from(random));
		const deciphered = pkcs7Pad(Buffer.concat([
			Buffer.from(random),
			msgLength,
			msg,
			Buffer.from(id),
		]));
		const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
		cipher.setAutoPadding(false);
		const ciphered = Buffer.concat([
			cipher.update(deciphered),
			cipher.final(),
		]);
		// 返回加密数据的base64编码
		return ciphered.toString('base64');
	},
	/**
	 * aes-256-cbc解密
	 * @param encodingAESKey EncodingAESKey
	 * @param encrypt        密文
	 */
	aes256cbcDecrypt: function(encodingAESKey, encrypt) {
		const {
			key,
			iv
		} = parseEncodingAESKey(encodingAESKey);
		const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
		decipher.setAutoPadding(false);
		const deciphered = pkcs7Unpad(Buffer.concat([
			decipher.update(encrypt, 'base64'),
			decipher.final(),
		]));
		// 算法：AES_Encrypt[random(16B) + msg_len(4B) + msg + $CorpID]
		const length = deciphered.readUInt32BE(16);
		return {
			message: deciphered.slice(16 + 4, length + 16 + 4).toString(),
			id: deciphered.slice(length + 20).toString(),
			random: deciphered.slice(0, 16),
		};
	}
}
